#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <stdbool.h>
#include <dlfcn.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h> 
#include "virtual-machine/ram.c"
#include "virtual-machine/opcodes.h"
#include "gex4.h"
#include "vild/dasm.c"



/*
 * Here are our signal handlers, I only
 * implemented SIGSEGV and SIGINT
 */
void segFault()
{
	printf("*** Fatal: Segmentation fault! ***\n");
	printf("Viper has crashed! This is most likely due to a VM bug\n");
        printf("not a programming bug, if you see GruntTheDivine tell\nhim this.\n\n");
	char **strings;
	void *array[10];
	/*
	size_t size = backtrace(array, 10);
	strings = backtrace_symbols(array,size);
	int i = 0;
	for(i; i < size;i++)
		printf("%s\n",strings[i]);
	*/
	abort();
}

void sigInt()
{
	printf("\nProgram has been terminated!\n");
	abort();
}
void sigInit()
{
	signal(SIGSEGV,segFault);
	signal(SIGINT,sigInt);
}


int main(int argc, char* argv[])
{
	sigInit();
	FILE* bin;
	bool useGex = false;
	
	int org = 0;	// org = orign, the address were the program gets loaded
	int ram = 0xFFFF;	 // how much RAM to give our program
	if(argc < 2)
	{
		printf("Invalid usage!\n\n");
		printf("Synposis:\n");
		printf("viper <program> [arguments]\n");
		return 0;
	}
	else if (argc >= 2)
	{
		if(strcmp("-h",&argv[1][0]) == 0 || strcmp("-help",&argv[1][0]) == 0 || strcmp("-?",&argv[1][0]) == 0)
		{
			printf("Usage:\n\n");
			printf("viper <program> [options]\n\n");
			printf("-org			Change origin\n");
			printf("-r			Change memory\n");
			printf("-ram			Change memory\n");
			printf("-s			Change stack address\n");
			printf("-h			Change heap address\n");
			printf("-gex			Load GEX4 program\n");
			return 0;
		}
		
		int i = 2;
		for(i; i < argc;i++)
		{
			if(strcmp("-org",&argv[i][0]) == 0 || strcmp("--org",&argv[i][0]) == 0)
			{
				++i;
				char* sorg = (char*)&argv[i][0];
				org = atoi(sorg);
			}
			else if(strcmp("-gex",&argv[i][0]) == 0 || strcmp("--gex",&argv[i][0]) == 0)
			{
				useGex = true;
			}
			else if(strcmp("-r",&argv[i][0]) == 0 || strcmp("--r",&argv[i][0]) == 0 || strcmp("-ram",&argv[i][0]) == 0)
			{
				++i;
				char* sram = (char*)&argv[i][0];
				ram = atoi(sram);
			}
			else
			{
				printf("'%s', no such option! Try -h for help\n",&argv[i][0]);
				return 0;
			}
		}
	}
	// If in debug mode we must wait for a debugger to connect....
		char x;
    	int xpos = 0;
	create(ram); // Create will initilize the viper virtual machine
	
	
	// This part is assuming we are using a blain binary, opposed to a GEX file

	char* GEX = (char*)openGEX(argv[1]);
	GEXHdr* hdr = (GEXHdr*)GEX;
	int textSize = 0;
	org = 0;
	theProgram = GEX;
	if(strncmp("GEX4 SC",hdr->magic,7) == 0)
	{
		for(int i = 0; i < hdr->sectionCount;i++)
		{
			GEXShdr* section = (GEXShdr*)(GEX + (hdr->sectionTable + (i * sizeof(GEXShdr))));
			if(strncmp(section->name,".text",5) == 0)
			{
				textSize = section->length;
				memcpy(RAM + org,GEX + section->ptr,section->length);
				heapPointer += section->length;
			}
			else if (strncmp(section->name,".data",5) == 0)
			{
				memcpy(RAM + org + textSize ,GEX + section->ptr,section->length );
				heapPointer += section->length;
			}
		}
		heapPointer += org;

		runCode(org);
		/*
		FILE* dmp = fopen("ram.dmp","w");
		fwrite(RAM,1,1024,dmp);
		fclose(dmp);
		*/
		
	}
	else
	{
		printf("Error: Invalid GEX Header! Aborting!\n");
		return 0; 
	}
	
	return 0;

}
